package Math;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Mouchicc
 * 
 */
public class Function {

	public String function = null;
	public double input;

	public Function(String f) {
		this.function = f;
	}

	public Function(String f, double i) {
		this.function = f;
		this.input = i;
	}

	public void setInput(double i) {
		this.input = i;
	}

	public String getFunction() {
		return function;
	}

	public double getInput() {
		return input;
	}

	public String getDifferential() {
		// Differential diff = new Differential(
		return null;
	}

	public String getPrimitive() {
		return null;
	}

	// ----------------- METHODS --------------- \\

	/**
	 * @param s
	 *            Part of a function
	 * @return The letter found in the String else return null
	 */
	public String getLetter(String s) {
		if (s.contains("sin") || s.contains("cos")) {
			String p = s.substring(s.indexOf("("));
			for (int i = 0; i < p.length(); i++) {
				if (Character.isLetter(p.charAt(i)) && p.charAt(i) != 'P'
						&& p.charAt(i) != 'I') {
					return Character.toString(p.charAt(i));
				}
			}
		} else {
			for (int i = 0; i < s.length(); i++) {
				if (Character.isLetter(s.charAt(i))) {
					return Character.toString(s.charAt(i));
				}
			}
		}
		return null;
	}

	public String differentiateNormal(String s) {
		String out = null;
		String strMacht = "";
		String strGetal = "";
		String diffMacht = "";
		String diffGetal = "";

		int LM = 0, RM = 0;
		double macht = 0;

		// Get the letter used in this part of the function.
		if (getLetter(s) != null) {
			String symbol = getLetter(s);

			if (s.contains("^")) {
				strMacht = s.split("\\^")[1];
			} else {
				strMacht = "1";
			}

			if (s.startsWith(symbol)) {
				strGetal = "1";
			} else {
				strGetal = s.split(symbol)[0];
			}

			// Differntiate macht:
			if (strMacht.contains("(")) { // Does the power value contain a '('
				LM = Integer.parseInt(strMacht.split("/")[0].replace("(", ""));
				RM = Integer.parseInt(strMacht.split("/")[1].replace(")", ""));
				int diffLM = LM - RM;
				macht = ((double) diffLM / (double) RM);
				if (Methods.isDecimal(macht)) {
					diffMacht = "(" + diffLM + "/" + RM + ")";
				} else {
					diffMacht = Methods.simplifyDouble(macht);
				}
			} else { // The power value doesn't contain a '('
				LM = Integer.parseInt(strMacht);
				RM = 1;
				int idMacht = Integer.parseInt(strMacht) - 1;
				diffMacht = Integer.toString(idMacht);
			}

			// Differntiate getal:
			if (strGetal.contains("(")) {
				int LG = Integer.parseInt(strGetal.split("/")[0].replace("(",
						""));
				int RG = Integer.parseInt(strGetal.split("/")[1].replace(")",
						""));
				double dobGetal = ((double) LG * (double) LM)
						/ ((double) RG * (double) RM);
				if (Methods.isInfinite(dobGetal)) {
					diffGetal = "(" + (int) ((double) LG * (double) LM) + "/"
							+ (int) ((double) RG * (double) RM) + ")";
				} else {
					diffGetal = Methods.simplifyDouble(dobGetal);
				}
			} else {
				double dobGetal = (Integer.parseInt(strGetal) * (double) LM)
						/ (double) RM;
				if (Methods.isInfinite(dobGetal)) {
					diffGetal = "(" + (Integer.parseInt(strGetal) * LM) + "/"
							+ RM + ")";
				} else {
					diffGetal = Methods.simplifyDouble(dobGetal);
				}
			}

			if (!diffGetal.contains("(") && Double.parseDouble(diffGetal) == 1) {
				if (!strGetal.contains("(") && Integer.parseInt(strGetal) != 1) {
					diffGetal = "";
				}
			}

			if (!diffMacht.contains("(")) {
				if (Integer.parseInt(diffMacht) == 1) {
					out = diffGetal + symbol;
				} else if (Integer.parseInt(diffMacht) == 0) {
					out = diffGetal;
				}
			}
			if (out == null) {
				out = diffGetal + symbol + "^" + diffMacht;
			}
		} else {
			return "";
		}
		return out;
	}

	public String differentiateCombinatoriek(String s) {
		String leftPart = "";
		String diff = "";
		String out = "";
		String pm;

		if (getLetter(s) != null) {
			String symbol = getLetter(s);
			pm = Character.toString(s.charAt(0));
			if (s.contains("sin(")) {
				leftPart = s.replace("sin(", "").replace(")", "")
						.replace(symbol, "");
				diff = s.replace("sin", "cos");
				// out = leftPart + diff;
			} else if (s.contains("cos(")) {
				leftPart = s.replace("cos(", "").replace(")", "")
						.replace(symbol, "");
				diff = s.replace("cos", "sin");
				Methods.log("Diff: " + diff);
				// out = "-" + leftPart + diff;
			}
			diff = diff.replace("-", "").replace("+", "");
			out = pm + leftPart + diff;
		} else {
			return "";
		}
		return out;
	}

	public String differentiate() {
		String out = "";
		String[] parts = null;

		// Split the function in parts.
		if (function.contains("+") || function.contains("-")) {
			parts = splitFunction();
		} else {
			parts = new String[] { function };
		}

		for (int i = 0; i < parts.length; i++) {
			String d = null;
			if (parts[i].contains("sin") || parts[i].contains("cos")) {
				d = differentiateCombinatoriek(parts[i]);
			} else {
				if (parts[i].contains("*")) {
					String[] fg = parts[i].split("\\*");
					d = "(" + fg[0] + "*" + differentiateNormal(fg[1]) + ")"
							+ "+" + "(" + differentiateNormal(fg[0]) + "*"
							+ fg[1] + ")";
				} else {
					d = differentiateNormal(parts[i]);
				}
			}
			if (i > 0 && d != null) {
				if (!d.startsWith("+") && !d.startsWith("-")) {
					out += "+";
				}
			}
			out += d;
		}
		return makeCorrect(out);
	}

	/**
	 * Method to split a function. Made by: Mouchicc
	 */
	public String[] splitFunction() {
		String[] splitted = function.split("(\\+|-)");
		String symbols = "";
		ArrayList<String> res = new ArrayList<String>();
		for (int i = 0; i < function.length(); i++) {
			if (function.charAt(i) == '+' || function.charAt(i) == '-') {
				symbols += Character.toString(function.charAt(i));
			}
		}
		res.add(splitted[0]);
		for (int j = 1; j < splitted.length; j++) {
			splitted[j] = symbols.charAt(j - 1) + splitted[j];
			if (splitted[j].startsWith("+")) {
				splitted[j] = Methods.replace(splitted[j], "\\+", "");
			}
			splitted[j] = Methods.replace(splitted[j], "_", "-");
			res.add(splitted[j]);
		}
		String[] out = new String[res.size()];
		return res.toArray(out);
	}

	/**
	 * 
	 * @param s
	 *            The Function that needs to get corrected.
	 * @return --, -+ and such fixed to +, - etc.
	 */
	private String makeCorrect(String s) {
		s = Methods.replace(s, "\\s", "");
		String[] find = { "\\--", "\\+-", "\\-+", "\\++", };
		String[] replace = { "\\+", "\\-", "\\-", "\\+" };
		for (int j = 0; j < find.length; j++) {
			Pattern pattern = Pattern.compile(find[j]);
			Matcher matcher = pattern.matcher(s);
			s = matcher.replaceAll(replace[j]);
		}
		return s;
	}
}